package pl.koziolekweb.translator.engine.googleimpl;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

import net.sf.json.JSONObject;
import net.sf.json.JSONSerializer;
import net.sf.json.JsonConfig;
import pl.koziolekweb.translator.engine.Language;
import pl.koziolekweb.translator.engine.TranslatorService;
import pl.koziolekweb.translator.utilites.Context;
import pl.koziolekweb.translator.utilites.StringUtilites;

/**
 * 
 * Implemantation of {@link TranslatorService} that use Google Translate API to
 * translate texts.
 * 
 * @author koziolek
 * 
 * 
 */

// Info: $Id$
public class TranslatorImpl implements TranslatorService {

	/**
	 * correct addres should be [URL_STRING][FROM]%7C[TO][TEXT_VAR][TEXT] .
	 * Kodowanie żądania.
	 */
	private static final String ENCODING = "UTF-8";
	/**
	 * Adres żądania.
	 */
	private static final String URL_STRING = "http://ajax.googleapis.com/ajax/services/"
			+ "language/translate?v=1.0&langpair=";
	/**
	 * Zmianna zapytania.
	 */
	private static final String TEXT_VAR = "&q=";
	/**
	 * Nazwa dostawcy.
	 */
	private static final String NAME = "Google-Translate";

	/**
	 * Język domyślny.
	 */
	private final Language defaultLanguage;

	/**
	 * Ostatnia odpowiedź.
	 */
	private GoogleTranslateResponse lastResponse;

	/**
	 * Konstruktor domyśny ustawia język domyślny w trybie
	 * {@link GoogleLanguage#AUTO_DETECT}.
	 */
	public TranslatorImpl() {
		defaultLanguage = GoogleLanguage.AUTO_DETECT;
	}

	/**
	 * Konstruktor pozwala na ustanowienie języka domyślnego serwisu.
	 * 
	 * @param oDefaultLanguage
	 *            język domyślny.
	 */
	public TranslatorImpl(final Language oDefaultLanguage) {
		this.defaultLanguage = oDefaultLanguage;
	}

	/**
	 * Metoda tłumaczy zadany tekst na język domyślny. Język tekstu jest
	 * rozpoznawany automatycznie.
	 * 
	 * @param text
	 *            tekst do przetłumaczenia
	 * @return przetłumaczony tekst
	 * @throws Throwable
	 *             tłumaczenie może zwrócić wyjątek.
	 * 
	 */
	public final String translate(final String text) throws Throwable {
		return this.translate(text, GoogleLanguage.AUTO_DETECT, defaultLanguage);
	}

	/**
	 * Metoda służy do tłumaczenia tekstu w zadanym języku na język domyślny.
	 * 
	 * @param text
	 *            tekst do przetłumaczenia
	 * @param from
	 *            jezyk docelowy tłumaczenia
	 * @return przetłumaczony tekst
	 * @throws Throwable
	 *             tłumaczenie może zwrócić wyjątek.
	 */
	public final String translate(final String text, final Language from) throws Throwable {
		return this.translate(text, from, defaultLanguage);
	}

	/**
	 * Metoda służy do tłumaczenia tekstu w języku zadanym w parametrze
	 * <samp>from</samp> na język zadany w parametrze <samp>to</samp>.
	 * 
	 * @param text
	 *            tekst do przetłumaczenia
	 * @param from
	 *            język z ktorego następuje tłumaczenie.
	 * @param to
	 *            jezyk docelowy tłumaczenia
	 * @return przetłumaczony tekst
	 * @throws Throwable
	 *             tłumaczenie może zwrócić wyjątek.
	 */
	public final synchronized String translate(final String text, final Language from, final Language to)
			throws Throwable {
		final StringBuilder url = createURL(text, from, to);
		final HttpURLConnection uc = connect(url);
		final String result = getAsString(uc.getInputStream());
		final GoogleTranslateResponse response = deserializeResponse(result);

		lastResponse = response;

		if (lastResponse.getResponseData() == null) {
			try {
				return Context.getApplicationContext().getParam("null.translation");
			} catch (RuntimeException e) {
				return "null translation";
			}
		}
		return StringUtilites.unescapeHTML(lastResponse.getResponseData().getTranslatedText());
	}

	/**
	 * Zwraca ostatnią odpowiedź zerwera.
	 * 
	 * @return {@link GoogleTranslateResponse Odpowiedź}
	 */
	public final synchronized GoogleTranslateResponse getLastResponse() {
		return lastResponse;
	}

	/**
	 * Deserializuje odpowiedź.
	 * 
	 * @param result
	 *            Odpowiedź jako {@link String}
	 * @return Odpowiedź jako obiekt google.
	 */
	private GoogleTranslateResponse deserializeResponse(final String result) {
		final JSONObject jsonObject = (JSONObject) JSONSerializer.toJSON(result);
		final JsonConfig jsonConfig = new JsonConfig();
		jsonConfig.setRootClass(GoogleTranslateResponse.class);
		final GoogleTranslateResponse response = (GoogleTranslateResponse) JSONSerializer
				.toJava(jsonObject, jsonConfig);
		return response;
	}

	/**
	 * Create {@link HttpURLConnection connection} form url.
	 * 
	 * @param url
	 *            to connect for
	 * @return connection
	 * @throws IOException
	 *             throw when some I/O problem occured
	 */
	private HttpURLConnection connect(final StringBuilder url) throws IOException {
		final HttpURLConnection uc = (HttpURLConnection) new URL(url.toString()).openConnection();
		return uc;
	}

	/**
	 * Create valid {@link URL} from user data.
	 * 
	 * @param text
	 *            text to translate
	 * @param from
	 *            language from
	 * @param to
	 *            language to
	 * @return URL in correct google format.
	 * @throws UnsupportedEncodingException
	 *             when use unsuported encoding for url.
	 */
	private StringBuilder createURL(final String text, final Language from, final Language to)
			throws UnsupportedEncodingException {
		final StringBuilder url = new StringBuilder();
		url.append(TranslatorImpl.URL_STRING).append(from).append("%7C").append(to);
		url.append(TranslatorImpl.TEXT_VAR).append(URLEncoder.encode(text, TranslatorImpl.ENCODING));
		return url;
	}

	/**
	 * Create {@link String} form {@link InputStream}.
	 * 
	 * @param inputStream
	 *            input stream from server.
	 * @return response as {@link String}
	 * 
	 * @throws IOException
	 *             when some IO problems occured.
	 */
	private String getAsString(final InputStream inputStream) throws IOException {
		final StringBuilder outputBuilder = new StringBuilder();
		try {
			String string;
			if (inputStream != null) {
				final BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream,
						TranslatorImpl.ENCODING));
				while (null != (string = reader.readLine())) {
					outputBuilder.append(string).append('\n');
				}
			}
		} catch (final IOException e) {
			throw new IOException("[ERROR] I\\O Error reading translation stream.", e);
		}
		return outputBuilder.toString();
	}

	/**
	 * Method return name of service. Client could lookup for service by name
	 * using Service.getByName()
	 * 
	 * @return service name
	 */
	@Override
	public final String getServiceName() {
		return TranslatorImpl.NAME;
	}

	/**
	 * Method return list of supported languages.
	 * 
	 * @see pl.koziolekweb.translator.engine.Translator#getSupportedLanguages()
	 * @return supported lanuages list.
	 */
	@Override
	public final List<Language> getSupportedLanguages() {
		Language[] languages = pl.koziolekweb.translator.engine.googleimpl.GoogleLanguage.values();
		List<Language> languagesList = new ArrayList<Language>();
		for (Language l : languages) {
			languagesList.add(l);
		}
		return languagesList;
	}
}
